<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/ol.min.css"
    integrity="sha512-bc9nJM5uKHN+wK7rtqMnzlGicwJBWR11SIDFJlYBe5fVOwjHGtXX8KMyYZ4sMgSL0CoUjo4GYgIBucOtqX/RUQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer" />
  <title>点位渲染</title>

  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    html,
    body,
    #app,
    .app-map {
      height: 100%;
    }

    .app-btns {
      position: fixed;
      right: 10px;
      top: 10px;
      background-color: #fff;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
      /* width: 210px; */
      padding: 25px;
      text-align: center;
      border-radius: 5px;
      display: flex;
      flex-direction: column;
    }

    .app-btns button {
      font-size: 18px;
      border: none;
      padding: 12px 20px;
      border-radius: 4px;
      color: #fff;
      background-color: #409eff;
      border-color: #409eff;
      cursor: pointer;
      border: 1px solid #dcdfe6;
      margin-bottom: 5px;
    }

    .app-btns button:hover {
      background-color: rgba(7, 193, 96, 0.8);
    }

    .app-btns button.active {
      background-color: #07c160;
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="app-map" id="app-map"></div>
    <div class="app-btns">
      <button type='button' :class='{active: type === 1}'
        @click='handleClickAddFeatures'>加载方式1（通过addFeatures方法添加的feature）</button>
      <button type='button' :class='{active: type === 2}'
        @click='handleClickGeoJSON'>加载方式2（通过GeoJSON方法添加的feature）</button>
    </div>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/dist/ol.min.js"
    integrity="sha512-+nvfloZUX7awRy1yslYBsicmHKh/qFW5w79+AiGiNcbewg0nBy7AS4G3+aK/Rm+eGPOKlO3tLuVphMxFXeKeOQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.14/vue.global.prod.min.js"
    integrity="sha512-huEQFMCpBzGkSDSPVAeQFMfvWuQJWs09DslYxQ1xHeaCGQlBiky9KKZuXX7zfb0ytmgvfpTIKKAmlCZT94TAlQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>

  <script>
    const { createApp } = Vue;
    // feature图片
    const base64Img = '';
    // const base64Img = './assets/images/online.svg';
    // const base64Img = './assets/images/蝴蝶.png';
    // feature样式
    const monitorIcon = new ol.style.Style({
      image: new ol.style.Icon({
        src: base64Img,
        scale: 1,
        anchor: [0.5, 0.5],
        rotateWithView: true,
        rotation: 0,
        opacity: 1
      })
    });
    createApp({
      data() {
        return {
          type: 0, // 加载方式
          map: {}, // 地图实例
          currentMonitorLayer: null // 当前监控图层
        }
      },
      methods: {
        // 初始化地图
        initMap() {
          // 高德地图瓦片地址
          const vectorLayer = new ol.layer.Tile({
            source: new ol.source.XYZ({
              url: 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
            }),
            name: '初始化地图图层'
          });
          //  初始化地图
          this.map = new ol.Map({
            target: 'app-map',
            layers: [vectorLayer],
            view: new ol.View({
              projection: 'EPSG:3857',
              //设定中心点，因为默认坐标系为 3587，所以要将我们常用的经纬度坐标系4326 转换为 3587坐标系
              center: ol.proj.transform([105.4315021624999, 38.25918891134296], 'EPSG:4326', 'EPSG:3857'),
              zoom: 5,
            })
          });
          // 绑定地图事件
          this.bindMapEvt();
        },
        // 绑定地图事件
        bindMapEvt() {
          const { map } = this;
          // 监听鼠标移动，移动到feature上时，鼠标变为可点击的状态
          map.on('pointermove', (e) => {
            let pixel = map.getEventPixel(e.originalEvent);
            let feature = map.forEachFeatureAtPixel(pixel, (feature) => {
              return feature
            })
            if (feature == undefined || feature.getGeometry().getType() != 'Point') {
              map.getTargetElement().style.cursor = 'auto'
            } else {
              map.getTargetElement().style.cursor = 'pointer'
            }
          });
          // 监听鼠标点击
          map.on('click', (evt) => {
            let clickPoint = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
            console.log('当前点击坐标为 : ' + clickPoint[0].toFixed(7) + ',' + clickPoint[1].toFixed(7));
            // 根据像素位置获取所有图层上的要素（features）
            const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
              return feature;
            });
            if (feature) {
              // 获取自定义参数
              const customValue = feature.get('custom');
              // 获取feature上增加的fromLayerID字段
              const featureLayerID = feature.get('fromLayerID');
              // 获取所有图层
              const layers = map.getLayers().getArray();
              if (featureLayerID === 'addFeaturesLayer') {
                alert(`点击的feature的自定义参数是${customValue.id}，这个是通过addFeatures方法添加的feature，点击会将其删除。`);
                // 根据layerID获取feature所在的图层
                const addFeaturesLayer = layers.find(e => e.get('layerID') === featureLayerID);
                addFeaturesLayer.getSource().removeFeature(feature);
              };
              if (featureLayerID === 'geoJSONLayer') {
                alert(`点击的feature的自定义参数是${customValue.id}，这个是通过GeoJSON方法添加的feature，点击会将其删除。`);
                // 根据layerID获取feature所在的图层
                const geoJSONFeaturesLayer = layers.find(e => e.get('layerID') === featureLayerID);
                geoJSONFeaturesLayer.getSource().removeFeature(feature);
              };
            }
          })
        },
        // new ol.Feature()的方式定义feature
        handleClickAddFeatures() {
          this.type = 1;
          // 删除上一次的图层
          this.currentMonitorLayer && this.map.removeLayer(this.currentMonitorLayer);
          const { map } = this;
          const positions = this.createCircularPosition2(); // 生成数据
          console.time('new ol.Feature方式');
          // 根据positions创建一个新的数据源和要素数组，
          const vectorSource = new ol.source.Vector({
            features: positions.map(e => {
              // ol.proj.fromLonLat用于将经纬度坐标从 WGS84 坐标系转换为地图投影坐标系
              const feature = new ol.Feature({
                geometry: new ol.geom.Point(e),
                custom: {
                  id: Math.ceil(Math.random() * 100000)
                },
                fromLayerID: 'addFeaturesLayer'
              });
              feature.setStyle(monitorIcon);
              return feature;
            })
          });
          // 创建带有数据源的矢量图层
          const featuresLayer = new ol.layer.Vector({
            source: vectorSource,
            layerID: 'addFeaturesLayer',
            name: '点位图层1'
          });
          // 将矢量图层添加到地图上
          map.addLayer(featuresLayer);
          this.currentMonitorLayer = featuresLayer;
          console.timeEnd('new ol.Feature方式');
        },
        // 将feature添加到GeoJSON数据内，再加到地图上
        handleClickGeoJSON() {
          this.type = 2;
          // 删除上一次的图层
          this.currentMonitorLayer && this.map.removeLayer(this.currentMonitorLayer);
          const { map } = this;
          const positions = this.createCircularPosition(); // 生成数据
          // 创建JSON
          const monitorGeoJSON = {
            "type": "FeatureCollection",
            "features": []
          };
          console.time('GeoJSON方式');
          // 根据positions中的坐标数据，添加到JSON的features数组里面
          for (let i = 0; i < positions.length; i++) {
            monitorGeoJSON.features.push({
              "type": "Feature",
              "geometry": {
                "type": "Point",
                "coordinates": positions[i]
              },
              "properties": {
                "name": "监控点",
                "description": "这是一个监控点位",
                "fromLayerID": "geoJSONLayer",
                "custom": {
                  id: Math.ceil(Math.random() * 99999)
                }
              }
            })
          };
          // 读取JSON数组，创建矢量图层
          const geoJSONLayer = new ol.layer.Vector({
            source: new ol.source.Vector({
              features: new ol.format.GeoJSON().readFeatures(monitorGeoJSON, { featureProjection: 'EPSG:3857' })
            }),
            layerID: 'geoJSONLayer',
            name: '点位图层2'
          });
          // 给feature设置样式
          geoJSONLayer.getSource().forEachFeature(function (feature) {
            feature.setStyle(monitorIcon);
          });
          // 将矢量图层添加到地图上
          map.addLayer(geoJSONLayer);
          this.currentMonitorLayer = geoJSONLayer;
          console.timeEnd('GeoJSON方式');
        },
        // 创建圆形范围数据
        createCircularPosition() {
          const center = [108.55, 34.32];
          const circularManyPosition = [];
          for (let i = 0; i < 1000; i++) {
            // 生成随机半径在-20到20之间
            const radius = Math.random() * 40 - 20;
            // 生成随机角度
            const angle = Math.random() * 2 * Math.PI;
            // 计算新点的坐标
            const newX = center[0] + radius * Math.cos(angle);
            const newY = center[1] + radius * Math.sin(angle);
            // 将新点添加到数组
            circularManyPosition.push([newX, newY]);
          }
          return circularManyPosition;
        },
        // 创建数据矩形范围数据
        createCircularPosition2() {
          const [minLon, minLat, maxLon, maxLat] = this.map.getView().calculateExtent(this.map.getSize());
          const circularManyPosition = [];
          for (let i = 0; i < 1000; i++) {
            const lon = minLon + Math.random() * (maxLon - minLon);
            const lat = minLat + Math.random() * (maxLat - minLat);
            // 将新点添加到数组
            circularManyPosition.push([lon, lat]);
          }
          return circularManyPosition;
        }
      },
      mounted() {
        this.initMap();
      }
    }).mount('#app')
  </script>
</body>

</html>